PHP MVC Controllers
Knoppen staan op formulieren, en met die knoppen kan de gebruiker communiceren met de server. Maar de code, die de interactie met de gebruiker afhandelt, staat heel dikwijls overal versnipperd.
Heel wat redirects waarmee je van her naar der springt. Redirects raad ik af want moeilijk te debuggen. De afhandeling van de interactie staat waarlijk overal en nergens.
Hoe kan je in godsnaam de workflow nog volgen? Is het geen idee om alle interactie met de gebruiker op één plaats te centraliseren in wat in het MVC patroon, de controller wordt genoemd?
Wat gaan we doen?
We maken een controllerklasse met de methoden die als respons op de requests van de gebruikers uitgevoerd zullen worden.
Stappenplan
- Voorkennis: PHP Routing
De methodesplitUri
breekt de uri op in de naam van de controllerklasse, de actiemethode en een eventuele parameter. Misschien moeten we die methode hernoemen naargetRouting
. Vertalingen voor routing zijn: routeren ; routetoewijzing ; routering ; routing ; route-bepaling ; route ; reisweg ; vervoerweg ; routebepaling ; routekeuze. En dat is precies wat we in PHP routing hebben gedaan. Meer dus dan een URI splitsen. - In de routing zijn we uitgegaan van use cases. Met elke use case stemt bij manier van spreken een knop overeen. Ik heb het niet over systeem use cases maar over gebruikers use cases. We hebben een vaste manier om use cases op de url door te geven:
De elementen worden gescheiden door een schuine streep /.
- de naam van de entiteit en die verwijst naar een Controllerklasse
- de naam van de actiemethode;
- een
id
parameter;
- Voorbeelden:
- EventCategory/Index
- EventCategory/InsertingOne
- EventCategory/CreateOne
- De
getRouteData
methode (voordiensplitUri
) retourneert een associatieve array met daarin de volgende key-value paren:'controllerName'
: de naam van een controller klasse in de standaard namespace;'actionMethodeName'
: de naam van de methode die moet worden uitgevoerd;'parameterValue'
: eventuele parameter die actiemethode wordt doorgegeven;
- De controller klasse voor de entiteit
Admin
(dit is een voorbeeld, de definitieve Admin wordt later gemaakt)- We volgen de afspraken binnen ASP.NET MVC (zie: ASP.NET MVC - Conventie boven configuratie) en plaatsen deze controllerklasse in een bestand met de naam
Controllers/AdminController.php
. En nog volgens die conventie, zetten we het controller-bestand in de map Controllers in de webroot. - Klassennaam:
AdminController
: net zoals in ASP.NET MVC maken we een afspraak dat namen voor controllerklassen eindigen met Controller. - Namespace:
Fricfrac\Controllers
-
De actiemethode
Index
Deze methode moet alleen een view retourneren met de tegels erin die voor de Index pagina van de Admin getoond moeten worden. Deze view staat in de map Views/Admin/Index.php.
DeIndex
methode retourneert een anonieme functie die deinclude
voor de view pagina in de public/index.php zal uitvoeren.namespace Fricfrac\Controllers; class AdminController { public function index() { $model = array('title' => 'Admin Index'); $path = "Views/Admin/Index.php"; $view = function () use ($model, $path) { include($path); }; return $view; } }
We passen hier anomieme functies in php toe. Wil je hier meer over weten: PHP Anonymous Functions: What Are They, and Why Use Them?
- Bijna elke methode in een controllerklasse gaat zo'n
$view
moeten retourneren. Het is dan ook handig die altijd al voorhanden te hebben. Hier komt overerving van pas. We maken een basisklasse met de naamController
die deze methode bevat en waarvan alle andere controllers kunnen van overerven.- in de map vendor/threepennymvc maak je een bestand met de naam Controller.php met daarin:
<?php /** * Created by ModernWays * User: Jef Inghelbrecht * Date: 23/02/2020 * Time: 10:32 */ namespace ThreepennyMVC; class Controller { public function view($model = null, $path = null) { if (!isset($path)) { // zoals in ASP.NET gaan we er van uit dat // de view staat in een map met de naam // van de controller en dat het bestand // de naam heeft van de methode $folderName = str_replace('Controller', '', (new \ReflectionClass($this))->getShortName()); $fileName = ucfirst(debug_backtrace()[1]['function']); $path = 'Views' . DIRECTORY_SEPARATOR . $folderName . DIRECTORY_SEPARATOR . $fileName . '.php'; } $view = function () use ($model, $path) { // echo $path; include($path); }; return $view; } }
- De
AdminController
erft nu over van dezeController
klasse en kan nu deview
methode van de basisklasse gebruiken. Deze methode is static en we hoeven dus geen instantie van de klasse aan te maken om deze methode te kunnen gebruiken:<?php /** * Created by ModernWays * User: Jef Inghelbrecht * Date: 3/04/2020 * Time: 13:32 */ namespace Fricfrac\Controllers; class AdminController extends \ThreepennyMVC\Controller { public function index() { $model = array('title' => 'Admin index'); // het $path argument geven we niet mee // de view staat immers in de standaardmap: // De Views map, de naam van de controller en de indexmethode: // Views/Admin/Index.php return $this->view($model); } }
- in de map vendor/threepennymvc maak je een bestand met de naam Controller.php met daarin:
- De controller klasse voor de entiteit
Event
(dit is een voorbeeld, de definitieveEventController
klasse wordt later gemaakt)- Staat in de
Controllers
map - Naam: Controllers/EventController.php
- erft over van
\ThreepennyMVC/Controller
- de
index
actiemethode - de
updatingOne
actiemethode<?php /** * Created by ModernWays * User: Jef Inghelbrecht * Date: 3/04/2020 * Time: 13:32 */ namespace Fricfrac\Controllers; class EventController extends \ThreepennyMVC\Controller { public function index() { $model = array( 'tableName' => 'Event', 'error' => 'Geen', 'row' => array( 'Name' => '', 'Location' => '', 'StartDate' => '', 'StartTime' => '', 'EndDate' => '', 'EndTime' => '', 'Image' => '', 'Description' => '', 'OrganiserName' => '', 'OrganiserDescription' => '', 'EventCategoryId' => null, 'EventTopicId' => null, 'ListEventCategory' => null, 'ListEventTopic' => null ), 'list' => array( array('Id' => 1, 'Name' => 'PHP serieus', 'Location' => 'Antwerpen'), array('Id' => 1, 'Name' => 'Bob Dylan in café De Kat', 'Location' => 'Antwerpen'), array('Id' => 1, 'Name' => 'Boekvoorstelling Klaartje Schrijvers', 'Location' => 'Antwerpen'), array('Id' => 1, 'Name' => 'Javacscript serieus', 'Location' => 'Antwerpen') ) ); return $this->view($model); } public function updatingOne() { $model = array( 'tableName' => 'Event', 'error' => 'Geen', 'row' => array( 'Name' => 'PHP serieus', 'Location' => 'Antwerpen', 'Starts' => '2020-10-10 20:00', 'Ends' => '2020-10-11 22:00', 'Image' => 'images/php-serieus.png', 'Description' => 'Leren werken met ThreepennyMVC', 'OrganiserName' => 'Modern Ways', 'OrganiserDescription' => 'Teaching material', 'EventCategoryId' => 3, 'EventTopicId' => 4 ), 'listEventCategory' => array( array('Id' => 1, 'Name' => 'Appearance or Signing'), array('Id' => 2, 'Name' => 'Attraction Camp.'), array('Id' => 3, 'Name' => 'Trip or Retreat'), array('Id' => 4, 'Name' => 'Concert or Performance'), array('Id' => 5, 'Name' => 'Course, Training or Workshop') ), 'listEventTopic' => array( array('Id' => 1, 'Name' => 'Auto, Boat & Air'), array('Id' => 2, 'Name' => 'Business & Professional'), array('Id' => 3, 'Name' => 'Charities & Causes'), array('Id' => 4, 'Name' => 'Community & Culture'), array('Id' => 5, 'Name' => 'Family & Education') ), 'list' => array( array('Id' => 1, 'Name' => 'PHP serieus', 'Location' => 'Antwerpen'), array('Id' => 1, 'Name' => 'Bob Dylan in café De Kat', 'Location' => 'Antwerpen'), array('Id' => 1, 'Name' => 'Boekvoorstelling Klaartje Schrijvers', 'Location' => 'Antwerpen'), array('Id' => 1, 'Name' => 'Javacscript serieus', 'Location' => 'Antwerpen') ) ); return $this->view($model); }
- Staat in de
- We volgen de afspraken binnen ASP.NET MVC (zie: ASP.NET MVC - Conventie boven configuratie) en plaatsen deze controllerklasse in een bestand met de naam